/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.yarn;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.yarn.api.ApplicationConstants;
import org.apache.hadoop.yarn.api.records.LocalResource;
import org.apache.hadoop.yarn.api.records.LocalResourceType;
import org.apache.hadoop.yarn.api.records.LocalResourceVisibility;
import org.apache.hadoop.yarn.api.records.NodeReport;
import org.apache.hadoop.yarn.api.records.NodeState;
import org.apache.hadoop.yarn.client.api.YarnClient;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.util.ConverterUtils;
import org.apache.hadoop.yarn.util.Records;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.concurrent.ThreadSafe;

/**
 * Utility methods for working with Yarn.
 *
 * @deprecated since 2.0
 */
@Deprecated
@ThreadSafe
public final class YarnUtils {
  /** This tarball is generated by alluxio-yarn.sh. */
  public static final String ALLUXIO_TARBALL = "alluxio.tar.gz";
  public static final String ALLUXIO_SETUP_SCRIPT = "alluxio-yarn-setup.sh";

  private static final NodeState[] USABLE_NODE_STATES;

  static {
    List<NodeState> usableStates = Lists.newArrayList();
    for (NodeState nodeState : NodeState.values()) {
      if (!nodeState.isUnusable()) {
        usableStates.add(nodeState);
      }
    }
    USABLE_NODE_STATES = usableStates.toArray(new NodeState[usableStates.size()]);
  }

  /**
   * Returns the host names for all nodes in yarnClient's YARN cluster.
   *
   * @param yarnClient the client to use to look up node information
   * @return the set of host names
   */
  public static Set<String> getNodeHosts(YarnClient yarnClient) throws YarnException, IOException {
    ImmutableSet.Builder<String> nodeHosts = ImmutableSet.builder();
    for (NodeReport runningNode : yarnClient.getNodeReports(USABLE_NODE_STATES)) {
      nodeHosts.add(runningNode.getNodeId().getHost());
    }
    return nodeHosts.build();
  }

  /**
   * Creates a local resource for a file on HDFS.
   *
   * @param yarnConf YARN configuration
   * @param resource the path to a resource file on HDFS
   * @return the created local resource
   */
  public static LocalResource createLocalResourceOfFile(YarnConfiguration yarnConf,
      String resource) throws IOException {
    LocalResource localResource = Records.newRecord(LocalResource.class);

    Path resourcePath = new Path(resource);

    FileStatus jarStat = FileSystem.get(resourcePath.toUri(), yarnConf).getFileStatus(resourcePath);
    localResource.setResource(ConverterUtils.getYarnUrlFromPath(resourcePath));
    localResource.setSize(jarStat.getLen());
    localResource.setTimestamp(jarStat.getModificationTime());
    localResource.setType(LocalResourceType.FILE);
    localResource.setVisibility(LocalResourceVisibility.PUBLIC);
    return localResource;
  }

  /**
   * Enum representing types of containers run by the yarn setup script. The strings here correspond
   * with the strings in integration/yarn/bin/alluxio-yarn-setup.sh.
   */
  public enum YarnContainerType {
    APPLICATION_MASTER("application-master"),
    ALLUXIO_MASTER("alluxio-master"),
    ALLUXIO_WORKER("alluxio-worker"),
    ;

    private final String mName;

    YarnContainerType(String name) {
      mName = name;
    }

    /**
     * @return the name of the container type
     */
    public String getName() {
      return mName;
    }
  }

  /**
   * Convenience method for calling {@link #buildCommand(YarnContainerType, Map)} with no arguments.
   *
   * @param containerType the type of container to build the command for
   * @return the built command string
   */
  public static String buildCommand(YarnContainerType containerType) {
    return buildCommand(containerType, new HashMap<String, String>());
  }

  /**
   * Creates a command string for running the Alluxio yarn setup script for the given type of yarn
   * container.
   *
   * @param containerType the type of container to build the command for
   * @param args arguments to pass to to the setup script
   * @return the built command string
   */
  public static String buildCommand(YarnContainerType containerType, Map<String, String> args) {
    CommandBuilder commandBuilder =
        new CommandBuilder("./" + ALLUXIO_SETUP_SCRIPT).addArg(containerType.getName());
    for (Entry<String, String> argsEntry : args.entrySet()) {
      commandBuilder.addArg(argsEntry.getKey(), argsEntry.getValue());
    }
    // Redirect stdout and stderr to yarn log files
    commandBuilder.addArg("1>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stdout");
    commandBuilder.addArg("2>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stderr");
    return commandBuilder.toString();
  }

  private YarnUtils() {} // this utils class should not be instantiated
}
